package com.lz.yz.outing.service.impl;

import com.lz.yz.base.entity.ReturnValue;
import com.lz.yz.base.util.HttpClientUtils;
import com.lz.yz.member.entity.Account;
import com.lz.yz.member.entity.Member;
import com.lz.yz.member.entity.Withdraws;
import com.lz.yz.member.enumeration.WithdrawsStatus;
import com.lz.yz.member.service.AccountService;
import com.lz.yz.member.service.WithdrawsService;
import com.lz.yz.order.entity.Orders;
import com.lz.yz.order.enumeration.OrderStatus;
import com.lz.yz.order.service.OrdersService;
import com.lz.yz.outing.config.ConstantConfig;
import com.lz.yz.outing.config.InitConfig;
import com.lz.yz.outing.config.WechatConfig;
import com.lz.yz.outing.entity.PayReqData;
import com.lz.yz.outing.entity.PayResData;
import com.lz.yz.outing.entity.RefundResult;
import com.lz.yz.outing.entity.RefundVo;
import com.lz.yz.outing.enumeration.PlatformType;
import com.lz.yz.outing.enumeration.SettingType;
import com.lz.yz.outing.service.WechatService;
import com.lz.yz.outing.util.*;
import com.lz.yz.reward.dao.RewardMapper;
import com.lz.yz.reward.entity.Reward;
import com.lz.yz.reward.enumeration.RewardStatus;
import com.util.common.RequestHelper;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class WechatServiceImpl implements WechatService {

    Logger log = LoggerFactory.getLogger(WechatServiceImpl.class);
    @Autowired
    private InitConfig initConfig;
    @Autowired
    private ConstantConfig constantConfig;
    @Autowired
    private Signature signature;


    @Resource(name = "redisTemplate")
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    OrdersService ordersService;

    @Autowired
    WithdrawsService withdrawsService;

    @Autowired
    AccountService accountService;

    @Autowired
    RewardMapper rewardMapper;

    @Override
    public String  getTicket() {
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        if (ops.get("jsapi_ticket") != null){
            return ops.get("jsapi_ticket");
        }
        Map<String,String> map = new HashMap<>();
        String access_token = getAccessToken();
        String type = "jsapi";
        map.put("access_token",access_token);
        map.put("type",type);
        String param = Util.toUrlParam(map);
        String url = WechatConfig.TICKET_URL + "?" + param;
        JSONObject result = HttpClientUtils.httpGet(url);
        String jsapi_ticket = (String)result.get("ticket");
        if(null == ops.get("jsapi_ticket") && jsapi_ticket != null){
            ops.set("jsapi_ticket",jsapi_ticket,7200L, TimeUnit.SECONDS);
        }
        return jsapi_ticket;
    }

    @Override
    public String getAccessToken(){
        ValueOperations<String, String> ops = redisTemplate.opsForValue();
        if (ops.get(getAccessTokenCacheKey()) != null){
            return ops.get(getAccessTokenCacheKey());
        }
        Map<String,String> map = new HashMap<>();
        String appid = initConfig.getWechatAppid();
        String grant_type = "client_credential";
        String secret = initConfig.getWechatSecret();
        map.put("grant_type",grant_type);
        map.put("appid",appid);
        map.put("secret",secret);
        String param = Util.toUrlParam(map);
        String url = WechatConfig.PUBLIC_URL + "?" + param;
        JSONObject result = HttpClientUtils.httpGet(url);
        String access_token = (String)result.get("access_token");
        if(null == ops.get(getAccessTokenCacheKey()) && access_token != null){
            ops.set(getAccessTokenCacheKey(),access_token,7200L, TimeUnit.SECONDS);
        }
        return access_token;
    }

    private String getAccessTokenCacheKey(){
        return "wechat_access_token";
    }

    @Override
    public ReturnValue getSign(String url){
        String jsapi_ticket = getTicket();
        Map<String, Object> ret = Sign.sign(jsapi_ticket,url);
        return  ReturnValue.generateTrueReturnValue(ret);
    }

    /**
     *
     * @param platform 1公众号 2开放平台
     * @param code
     * @return
     */
    @Override
    public Member getUserInfoByCode(Integer platform,String code) {
        Member userInfo = null;
        String url =null;

        if(platform.equals(PlatformType.MP.value())){
            url = WechatConfig.ACCESS_TOKEN_URL + "?appid="+initConfig.getWechatAppid()+"&secret="+initConfig.getWechatSecret()+"&code="+code+"&grant_type=authorization_code";
        }else if(platform.equals(PlatformType.KF.value())){
            url = WechatConfig.ACCESS_TOKEN_URL + "?appid="+initConfig.getKfWechatAppid()+"&secret="+initConfig.getKfWechatSecret()+"&code="+code+"&grant_type=authorization_code";
        }
        JSONObject result = HttpClientUtils.httpGet(url);
        String access_token = (String) result.get("access_token");
        String openid = (String) result.get("openid");
        if(openid==null){
            log.error("code:"+code+"无效");
            return userInfo;
        }
        String urlInfo = WechatConfig.ACCESS_USER_INFO + "?openid="+openid+"&access_token="+access_token+"&lang=zh_CN";
        JSONObject resultInfo = HttpClientUtils.httpGet(urlInfo);
        if(resultInfo.get("errcode")!=null){
            log.error("openid:"+openid+"无效");
            return userInfo;
        }
        userInfo = new Member();
        userInfo.setOpenId((String)resultInfo.get("openid"));
        userInfo.setUnionId((String)resultInfo.get("unionid"));
        userInfo.setHeadPortrait((String)resultInfo.get("headimgurl"));
        userInfo.setRealName((String)resultInfo.get("nickname"));
        userInfo.setSex((Integer) resultInfo.get("sex"));
        return userInfo;
    }






    @Override
    public ReturnValue makeWechatOrder(Map<String, Object> map, HttpServletRequest request) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        String orderNo = (String) map.get("orderNo");
        String openId = (String) map.get("openId");
        String backParams = String.valueOf(map.get("backParams"));
        map.remove("orderNo");
        map.remove("openId");
        map.remove("backParams");
        Orders orderBean = ordersService.findBy("orderNo",orderNo);
        if(orderBean==null){
            return ReturnValue.generateFalseReturnValue("没有查到该订单");
        }
        Orders order = new Orders();
        BeanUtils.copyProperties(orderBean,order);

        Map<String, Object> responseMap = new HashMap<>();
        //生成随机数
        String random = ToolUtil.createRandomNumber(32);
        //支付签名时间戳
        String timestamp = Long.toString(System.currentTimeMillis() / 1000);

        map.put("out_trade_no",orderNo);
        map.put("total_fee",constantConfig.getSetting().equals(SettingType.PROD.value())?order.getTotalPrice().multiply(new BigDecimal(100)).intValue():order.getTotalPrice().multiply(new BigDecimal(100)).intValue());
        map.put("spbill_create_ip",RequestHelper.getStringIP(request));
        map.put("body", "公众号支付");
        map.put("appid", initConfig.getWechatAppid());
        map.put("mch_id", initConfig.getMchId());
        map.put("nonce_str", random);
        map.put("notify_url", initConfig.getWechatNotify());
        map.put("trade_type", "JSAPI");
        map.put("openid",openId);
        map.put("attach",backParams);
        String sign = signature.getSign(map,initConfig.getGzhKey());

        try {
            PayReqData reqData = new PayReqData();
            reqData.setAppid(initConfig.getWechatAppid());
            reqData.setMch_id( initConfig.getMchId());
            reqData.setNonce_str(random);
            reqData.setSign(sign);
            reqData.setBody((String) map.get("body"));
            reqData.setOut_trade_no((String) map.get("out_trade_no"));
            reqData.setTotal_fee(Integer.valueOf(map.get("total_fee").toString()));
            reqData.setSpbill_create_ip((String)map.get("spbill_create_ip"));
            reqData.setNotify_url((String) map.get("notify_url"));
            reqData.setTrade_type("JSAPI");
            reqData.setOpenid((String) map.get("openid"));
            reqData.setAttach(backParams);

            String result = HttpsRequest.sendPost(WechatConfig.UNIFIEDORDER_URL, reqData);

            PayResData payResData = (PayResData) Util.getObjectFromXML(result, PayResData.class);
            if (payResData == null || payResData.getReturn_code() == null) {
                log.warn("【支付失败】支付请求逻辑错误，请仔细检测传过去的每一个参数是否合法，或是看API能否被正常访问:" + result);
                return ReturnValue.generateFalseReturnValue(result);
            }
            if (payResData.getReturn_code().equals("FAIL")) {
                //注意：一般这里返回FAIL是出现系统级参数错误，请检测Post给API的数据是否规范合法
                log.warn("【支付失败】支付API系统返回失败，请检测Post给API的数据是否规范合法:" + result);
                return ReturnValue.generateFalseReturnValue(result);
            } else {
                //--------------------------------------------------------------------
                //收到API的返回数据的时候得先验证一下数据有没有被第三方篡改，确保安全
                //--------------------------------------------------------------------
                if (!signature.checkIsSignValidFromResponseString(result,initConfig.getGzhKey())) {
                    log.warn("【支付失败】支付请求API返回的数据签名验证失败，有可能数据被篡改了:" + result);
                    return ReturnValue.generateFalseReturnValue(result);
                }

                //获取错误码
                String errorCode = payResData.getErr_code();
                //获取错误描述
                String errorCodeDes = payResData.getErr_code_des();

                if (payResData.getResult_code().equals("SUCCESS")) {
                    //--------------------------------------------------------------------
                    //1)直接扣款成功
                    //--------------------------------------------------------------------
                    String prePayId = payResData.getPrepay_id();
                    if (prePayId != null) {
                        responseMap.put("package", "prepay_id="+prePayId);
                    }

                } else {
                    //出现业务错误
                    log.error("err_code", errorCode);
                    log.error("err_code_des", errorCodeDes);
                    return ReturnValue.generateFalseReturnValue("业务返回失败",Integer.valueOf(errorCode));
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        responseMap.put("appId", initConfig.getWechatAppid());
        responseMap.put("timeStamp", timestamp);
        responseMap.put("nonceStr", random);
        responseMap.put("signType", "MD5");
        sign =  signature.getSign(responseMap,initConfig.getGzhKey());
        responseMap.put("paySign", sign);
        return ReturnValue.generateTrueReturnValue(responseMap);
    }





    /**
     * 微信退款
     */
    @Override
    public ReturnValue wechatPayRefund(String orderNo){
        try {
            Orders  orderBean = ordersService.findBy("orderNo",orderNo);
            if(orderBean==null){
                return ReturnValue.generateFalseReturnValue("没有查到该订单");
            }
            Map<String, Object> map = new HashMap<>();
            Orders order = new Orders();
            BeanUtils.copyProperties(orderBean,order);
            //生成随机数
            String random = ToolUtil.createRandomNumber(32);

            map.put("out_trade_no",orderNo);
            map.put("total_fee",order.getTotalPrice().multiply(new BigDecimal(100)).intValue());
            map.put("appid", initConfig.getWechatAppid());
            map.put("mch_id", initConfig.getMchId());
            map.put("nonce_str", random);
            map.put("refund_fee", order.getTotalPrice().multiply(new BigDecimal(100)).intValue());
            map.put("out_refund_no", "tk"+orderNo);
            String sign = signature.getSign(map,initConfig.getGzhKey());

            RefundVo reqData = new RefundVo();
            reqData.setAppid(initConfig.getWechatAppid());
            reqData.setMch_id(initConfig.getMchId());
            reqData.setNonce_str(random);
            reqData.setOut_trade_no((String) map.get("out_trade_no"));
            reqData.setTotal_fee(Integer.valueOf(map.get("total_fee").toString()));
            reqData.setRefund_fee(Integer.valueOf(map.get("refund_fee").toString()));
            reqData.setOut_refund_no("tk"+orderNo);
            reqData.setSign(sign);

            String result = HttpsRequest.executeBySslPost(initConfig.getMchId(),WechatConfig.ORDERREFUND_URL, reqData,constantConfig.getSetting().equals(SettingType.PROD.value())?"apiclient_cert.p12":"apiclient_cert_test.p12");

            RefundResult payResData = (RefundResult) Util.getObjectFromXML(result, RefundResult.class);
            if (payResData == null || payResData.getReturn_code() == null) {
                log.warn("【支付失败】支付请求逻辑错误，请仔细检测传过去的每一个参数是否合法，或是看API能否被正常访问:" + result);
                return ReturnValue.generateFalseReturnValue(result);
            }
            if (payResData.getReturn_code().equals("FAIL")) {
                //注意：一般这里返回FAIL是出现系统级参数错误，请检测Post给API的数据是否规范合法
                log.warn("【支付失败】支付API系统返回失败，请检测Post给API的数据是否规范合法:" + result);
                return ReturnValue.generateFalseReturnValue(result);
            } else {
                //--------------------------------------------------------------------
                //收到API的返回数据的时候得先验证一下数据有没有被第三方篡改，确保安全
                //--------------------------------------------------------------------
                if (!signature.checkIsSignValidFromResponseString(result,initConfig.getGzhKey())) {
                    log.warn("【支付失败】支付请求API返回的数据签名验证失败，有可能数据被篡改了:" + result);
                    return ReturnValue.generateFalseReturnValue(result);
                }
                //获取错误码
                String errorCode = payResData.getErr_code();
                //获取错误描述
                String errorCodeDes = payResData.getErr_code_des();
                if (payResData.getResult_code().equals("SUCCESS")) {
                    //--------------------------------------------------------------------
                    //1)退款成功 修改订单状态
                    //--------------------------------------------------------------------
                    ordersService.update(new Orders(orderBean.getId(), OrderStatus.REFUND.getStatusValue()));
                    rewardMapper.updateByPrimaryKeySelective(new Reward(orderBean.getObjectiveId(), RewardStatus.CANCEL.getStatusValue()));
                    return ReturnValue.generateTrueReturnValue();
                } else {
                    log.error("err_code", errorCode);
                    log.error("err_code_des", errorCodeDes);
                    return ReturnValue.generateFalseReturnValue("业务返回失败",Integer.valueOf(errorCode));
                }
            }

        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return ReturnValue.generateFalseReturnValue("业务返回失败");
    }


    /**
     * 企业付款--提现
     */
    @Override
    public ReturnValue wechatTransfers(Map<String, Object> map, HttpServletRequest request){
        Integer withdrawsId = (Integer) map.get("withdrawsId");
        map.remove("withdrawsId");

        Withdraws withdraws = withdrawsService.findById(withdrawsId);
        if(withdraws == null){
            return ReturnValue.generateFalseReturnValue("没有查到该提现记录");
        }
        Account account = accountService.findById(withdraws.getAccountId());
        if(account == null){
            return ReturnValue.generateFalseReturnValue("无效的提现发起人");
        }
        String openId = account.getWechatToken();
        //生成随机数
        String random = ToolUtil.createRandomNumber(32);

        Map<String,Object> parameters = new HashMap<>();
        parameters.put("mch_appid", initConfig.getWechatAppid());	//微信分配的账号ID（企业号corpid即为此appId）
        parameters.put("mchid", initConfig.getMchId());	//微信支付分配的商户号
        parameters.put("nonce_str", random);
        parameters.put("partner_trade_no",withdraws.getWithdrawNo());
        parameters.put("openid",openId);
        parameters.put("check_name","NO_CHECK");//NO_CHECK：不校验真实姓名	FORCE_CHECK：强校验真实姓名
        parameters.put("amount", withdraws.getAmount().multiply(new BigDecimal(100)).intValue());
        parameters.put("desc","提现成功");
        parameters.put("spbill_create_ip", RequestHelper.getStringIP(request));	//调用接口的机器Ip地址

        String sign = signature.getSign(parameters,initConfig.getGzhKey());

        try {
            PayReqData reqData = new PayReqData();
            reqData.setMch_appid(initConfig.getWechatAppid());
            reqData.setMchid(initConfig.getMchId());
            reqData.setNonce_str(random);
            reqData.setPartner_trade_no((String) parameters.get("partner_trade_no"));
            reqData.setAmount(Integer.valueOf(parameters.get("amount").toString()));
            reqData.setOpenid((String) parameters.get("openid"));
            reqData.setCheck_name((String) parameters.get("check_name"));
            reqData.setSpbill_create_ip((String) parameters.get("spbill_create_ip"));
            reqData.setDesc((String) parameters.get("desc"));
            reqData.setSign(sign);
            String result = HttpsRequest.executeBySslPost(initConfig.getMchId(),WechatConfig.TRANSFERS_URL, reqData,constantConfig.getSetting().equals(SettingType.PROD.value())?"apiclient_cert.p12":"apiclient_cert_test.p12");
            RefundResult payResData = (RefundResult) Util.getObjectFromXML(result, RefundResult.class);
            if (payResData == null || payResData.getReturn_code() == null) {
                log.warn("【支付失败】支付请求逻辑错误，请仔细检测传过去的每一个参数是否合法，或是看API能否被正常访问:" + result);
                return ReturnValue.generateFalseReturnValue(result);
            }
            if (payResData.getReturn_code().equals("FAIL")) {
                log.warn("【支付失败】支付API系统返回失败，请检测Post给API的数据是否规范合法:" + result);
                return ReturnValue.generateFalseReturnValue(result);
            } else {
                if (!signature.checkIsSignValidFromResponseString(result,initConfig.getGzhKey())) {
                    log.warn("【支付失败】支付请求API返回的数据签名验证失败，有可能数据被篡改了:" + result);
                    return ReturnValue.generateFalseReturnValue(result);
                }
                //获取错误码
                String errorCode = payResData.getErr_code();
                //获取错误描述
                String errorCodeDes = payResData.getErr_code_des();
                if (payResData.getResult_code().equals("SUCCESS")) {
                    //--------------------------------------------------------------------
                    //1)提现成功
                    //--------------------------------------------------------------------
                    withdrawsService.update(new Withdraws(withdrawsId, WithdrawsStatus.SUCCESS.getStatusValue()));
                } else {
                    log.error("err_code", errorCode);
                    log.error("err_code_des", errorCodeDes);
                    withdrawsService.update(new Withdraws(withdrawsId,WithdrawsStatus.FAILURE.getStatusValue()));
                    return ReturnValue.generateFalseReturnValue("业务返回失败",Integer.valueOf(errorCode));
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        withdrawsService.update(new Withdraws(withdrawsId,WithdrawsStatus.FAILURE.getStatusValue()));
        return ReturnValue.generateFalseReturnValue("业务返回失败");
    }


}
